package com.store.orm.mysql.crud.filter;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;

import static com.store.orm.common.KeyWord.*;

/**
 * 过滤条件, 在 MySQL 数据 查询、修改、删除中使用
 * 提示：当过滤条件超过一个时必须使用连接操作符
 *
 * @author 宫静雨
 * @version 1.0
 */
public final class Filtration {

    private StringBuilder sb = new StringBuilder();

    public Filtration() {
        sb.append(WHERE);
    }

    /**
     * 分页操作
     *
     * @param start 开始条数
     * @param size  数量
     * @return 返回当前被使用的对象
     */
    public Filtration limit(Integer start, Integer size) {
        checkAndClearFiltration();
        sb.append(LIMIT).append(start).append(COMMA).append(size);
        return this;
    }

    /**
     * 排序操作
     *
     * @param field    依据字段
     * @param operator 排序方式：ASC、DESC
     * @return 返回当前被使用的对象
     */
    public Filtration orderBy(String field, Operator operator) {
        checkAndClearFiltration();
        sb.append(ORDER_BY).append(field);
        switch (operator) {
            case ASC:
                sb.append(Operator.ASC);
                break;
            case DESC:
                sb.append(Operator.DESC);
                break;
            default:
                throw new RuntimeException("传递的参数错误, 只支持 ASC|DESC");
        }
        return this;
    }

    /**
     * 排序操作
     *
     * @param field 依据字段
     * @return 返回当前被使用的对象
     */
    public Filtration orderBy(String field) {
        return orderBy(field, Operator.ASC);
    }

    /**
     * 检查 Filtration 是否带有其他条件, 如果有就不操作,
     * 否则就删除掉 WHERE
     */
    private void checkAndClearFiltration() {
        if (sb.length() == WHERE.length()) {
            sb.delete(0, sb.length());
        }
    }

    /**
     * 比较是否相等
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration eq(String field, Object value) {
        comparison(field, value, Operator.EQ);
        return this;
    }

    /**
     * 比较是否不相等
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration ne(String field, Object value) {
        comparison(field, value, Operator.NE);
        return this;
    }

    /**
     * 比较是否大于
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration gt(String field, Object value) {
        comparison(field, value, Operator.GT);
        return this;
    }

    /**
     * 比较是否大于等于
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration gte(String field, Object value) {
        comparison(field, value, Operator.GTE);
        return this;
    }

    /**
     * 比较是否小于
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration lt(String field, Object value) {
        comparison(field, value, Operator.LT);
        return this;
    }

    /**
     * 比较是否小于等于
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration lte(String field, Object value) {
        comparison(field, value, Operator.LTE);
        return this;
    }

    /**
     * 比较是否完全不相等
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration ane(String field, Object value) {
        comparison(field, value, Operator.ANE);
        return this;
    }

    /**
     * 连接操作符，使用 and 连接前几个条件，示例:
     * new Filtration().eq("id","1").le("age",22).and();
     *
     * @return 返回当前被使用的对象
     */
    public Filtration and() {
        sb = new StringBuilder(sb.toString().replaceAll("#", Operator.AND.operator));
        sb.delete(sb.lastIndexOf(AND), sb.length() - 1);
        sb.insert(sb.lastIndexOf(WHERE) + WHERE.length(), LEFT_ROUND_BRACKET + BLANK);
        sb.append(RIGHT_ROUND_BRACKET).append(POUND);
        return this;
    }

    /**
     * 连接操作符，使用 or 连接前几个条件，示例:
     * new Filtration().eq("id","1").le("age",22).or();
     *
     * @return 返回当前被使用的对象
     */
    public Filtration or() {
        sb = new StringBuilder(sb.toString().replaceAll("#", Operator.OR.operator));
        sb.delete(sb.lastIndexOf(OR), sb.length() - 1);
        sb.insert(sb.lastIndexOf(WHERE) + WHERE.length(), LEFT_ROUND_BRACKET + BLANK);
        sb.append(RIGHT_ROUND_BRACKET).append(POUND);
        return this;
    }

    /**
     * 连接操作符，使用 not 连接前几个条件，示例:
     * new Filtration().eq("id","1").le("age",22).not();
     *
     * @return 返回当前被使用的对象
     */
    public Filtration not() {
        sb.insert(sb.lastIndexOf(WHERE) + WHERE.length(), Operator.NOT + BLANK);
        return this;
    }

    /**
     * 该字段是为空
     *
     * @param field 被比较的字段
     * @return 返回当前被使用的对象
     */
    public Filtration isNull(String field) {
        assertKey(field);
        sb.append(LEFT_ROUND_BRACKET).append(field).append(Operator.IS_NULL)
                .append(RIGHT_ROUND_BRACKET).append(POUND);
        return this;
    }

    /**
     * 该字段是不为空
     *
     * @param field 被比较的字段
     * @return 返回当前被使用的对象
     */
    public Filtration isNotNull(String field) {
        assertKey(field);
        sb.append(LEFT_ROUND_BRACKET).append(field).append(Operator.IS_NOT_NULL)
                .append(RIGHT_ROUND_BRACKET).append(POUND);
        return this;
    }

    /**
     * 该字段的值在 value 集合中
     *
     * @param field 被比较的字段
     * @param list  值集合
     * @return 返回当前被使用的对象
     */
    public Filtration in(String field, List<Object> list) {
        logic(field, list, Operator.IN);
        return this;
    }

    /**
     * 该字段的值不在 value 集合中
     *
     * @param field 被比较的字段
     * @param list  值集合
     * @return 返回当前被使用的对象
     */
    public Filtration notIn(String field, List<Object> list) {
        logic(field, list, Operator.NOT_INT);
        return this;
    }

    /**
     * 模糊匹配
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration like(String field, String value) {
        sb.append(LEFT_ROUND_BRACKET).append(field).append(Operator.LIKE)
                .append(SINGLE_QUOTES).append(value).append(SINGLE_QUOTES)
                .append(RIGHT_ROUND_BRACKET).append(POUND);
        return this;
    }

    /**
     * 模糊匹配取反
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration noLike(String field, String value) {
        sb.append(LEFT_ROUND_BRACKET).append(field).append(Operator.NOT_LIKE)
                .append(SINGLE_QUOTES).append(value).append(SINGLE_QUOTES)
                .append(RIGHT_ROUND_BRACKET).append(POUND);
        return this;
    }

    /**
     * 正则匹配
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration regexp(String field, String value) {
        sb.append(LEFT_ROUND_BRACKET).append(field).append(Operator.REGEXP)
                .append(SINGLE_QUOTES).append(value).append(SINGLE_QUOTES)
                .append(RIGHT_ROUND_BRACKET).append(POUND);
        return this;
    }

    /**
     * 正则匹配, 取反操作
     *
     * @param field 被比较的字段
     * @param value 被比较的值
     * @return 返回当前被使用的对象
     */
    public Filtration notRegexp(String field, String value) {
        sb.append(LEFT_ROUND_BRACKET).append(field).append(Operator.NOT_REGEXP)
                .append(SINGLE_QUOTES).append(value).append(SINGLE_QUOTES)
                .append(RIGHT_ROUND_BRACKET).append(POUND);
        return this;
    }

    /**
     * 范围数据
     *
     * @param field 被比较的字段
     * @param v1    左边的值
     * @param v2    右边的值
     * @return 返回当前被使用的对象
     */
    public Filtration betweenAnd(String field, Object v1, Object v2) {
        between(field, v1, v2, Operator.BETWEEN);
        return this;
    }

    /**
     * 范围数据取反
     *
     * @param field 被比较的字段
     * @param v1    左边的值
     * @param v2    右边的值
     * @return 返回当前被使用的对象
     */
    public Filtration NotBetweenAnd(String field, Object v1, Object v2) {
        between(field, v1, v2, Operator.NOT_BETWEEN);
        return this;
    }

    /**
     * 处理 between_and & not_between_and 运算的工具方法, 实例如下:
     * (id between 3 and 5)#
     * (id not between 3 and 5)#
     *
     * @param key      字段
     * @param val      值
     * @param operator 操作
     */
    private void between(String key, Object val, Object val2, Operator operator) {
        assertKey(key);
        sb.append(LEFT_ROUND_BRACKET).append(key).append(operator);
        betweenHelp(val);
        sb.append(Operator.AND);
        betweenHelp(val2);
        sb.append(RIGHT_ROUND_BRACKET).append(POUND);
    }

    /**
     * between 的帮助方法
     *
     * @param val 值
     */
    private void betweenHelp(Object val) {
        if (val instanceof Integer || val instanceof Long || val instanceof Float || val instanceof Double
                || val instanceof Byte || val instanceof Short || val instanceof BigInteger || val instanceof BigDecimal) {
            sb.append(val);
        } else if (val instanceof Date) {
            Date date = (Date) val;
            java.sql.Date time = new java.sql.Date(date.getTime());
            sb.append(SINGLE_QUOTES).append(time).append(SINGLE_QUOTES);
        } else {
            sb.append(SINGLE_QUOTES).append(val).append(SINGLE_QUOTES);
        }
    }

    /**
     * 处理 in & not in 运算的工具方法, 实例如下:
     * (id in (1,2,3))#
     * (id not in (1,2,3))#
     *
     * @param field    字段
     * @param list     值
     * @param operator 操作
     */
    private void logic(String field, List<Object> list, Operator operator) {
        assertKey(field);
        sb.append(LEFT_ROUND_BRACKET).append(field).append(operator).append(LEFT_ROUND_BRACKET);
        list.forEach(val -> {
            if (val instanceof Integer || val instanceof Long || val instanceof Float || val instanceof Double
                    || val instanceof Byte || val instanceof Short || val instanceof BigInteger || val instanceof BigDecimal) {
                sb.append(val).append(COMMA);
            } else if (val instanceof Date) {
                Date date = (Date) val;
                java.sql.Date time = new java.sql.Date(date.getTime());
                sb.append(SINGLE_QUOTES).append(time).append(SINGLE_QUOTES).append(COMMA);
            } else {
                sb.append(SINGLE_QUOTES).append(val).append(SINGLE_QUOTES).append(COMMA);
            }
        });
        sb.deleteCharAt(sb.length() - 1);
        sb.append(RIGHT_ROUND_BRACKET).append(RIGHT_ROUND_BRACKET).append(POUND);
    }

    /**
     * 处理比较运算的工具方法, 实例如下:
     * (id = 3)#
     * (id >= 3)#
     *
     * @param key      字段
     * @param val      值
     * @param operator 操作
     */
    private void comparison(String key, Object val, Operator operator) {
        assertKey(key);
        if (val instanceof Integer || val instanceof Long || val instanceof Float || val instanceof Double
                || val instanceof Byte || val instanceof Short || val instanceof BigInteger || val instanceof BigDecimal) {
            sb.append(LEFT_ROUND_BRACKET).append(key).append(operator).append(val).append(RIGHT_ROUND_BRACKET);
        } else if (val instanceof Date) {
            Date date = (Date) val;
            java.sql.Date time = new java.sql.Date(date.getTime());
            sb.append(LEFT_ROUND_BRACKET).append(key).append(operator).append(SINGLE_QUOTES)
                    .append(time).append(SINGLE_QUOTES).append(RIGHT_ROUND_BRACKET);
        } else {
            sb.append(LEFT_ROUND_BRACKET).append(key).append(operator).append(SINGLE_QUOTES)
                    .append(val).append(SINGLE_QUOTES).append(RIGHT_ROUND_BRACKET);
        }
        sb.append(POUND);
    }

    /**
     * 检查提供的字段是否不合规
     *
     * @param key 提供的字段
     */
    private void assertKey(String key) {
        if (key.contains("=")) {
            throw new RuntimeException("key 不能包含 = , 涉嫌SQL注入");
        }
    }

    /**
     * 获取最终的结果
     *
     * @return 条件结果
     */
    public String get() {
        int length = sb.length();
        if (length == WHERE.length()) {
            return "";
        } else {
            String filtration = sb.toString();
            if (filtration.contains(POUND)) {
                filtration = filtration.replaceAll(POUND, "");
            }
            return filtration;
        }
    }

    @Override
    public String toString() {
        return "Filtration{" +
                "sb=" + sb +
                '}';
    }
}